
#include "pstrlib.h"

#include <stdlib.h>
#include <string.h>

#undef  MAX
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))

#undef	MIN
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))

#define MY_MAXSIZE ((unsigned long) -1)

pstring* pstring_sized_new (long);
pstring* pstring_append_len (pstring*, char*, long);
static void pstring_maybe_expand (pstring*, long);
static inline long nearest_power (long, long); 

pstring*
pstring_new (char *init)
{

  pstring *string;
  int len;

  if (init == NULL || *init == '\0')
    string = pstring_sized_new (2);
  else 
   {
    len = strlen (init);
    string = pstring_sized_new (len + 2);

    pstring_append_len (string, init, len);
   }

  return string;

}

pstring*
pstring_sized_new (long dfl_size)
{

  pstring *string;
  
  string = malloc (sizeof (pstring));

  string->allocated_len = 0;
  string->len = 0;
  string->str = NULL;

  pstring_maybe_expand (string, MAX (dfl_size, 2));
  string->str[0] = 0;

  return string;

}

static void
pstring_maybe_expand (pstring *string, long len)
{

  if (string->len + len >= string->allocated_len)
   {
    string->allocated_len = nearest_power (1, string->len + len + 1);
    string->str = realloc (string->str, string->allocated_len);
   }

}

static inline long
nearest_power (long base, long num)
{

  long n;

  if (num > MY_MAXSIZE / 2)
    return MY_MAXSIZE;
  else
   {
    n = base;

    while (n < num)
      n <<= 1;
      
    return n;
   }

}

pstring*
pstring_insert_len (pstring *string, long pos, const char *val, long len)    
{

  long offset;
  long precount;

  if (len < 0)
    len = strlen (val);

  if (pos < 0)
    pos = string->len;

  if (val >= string->str && val <= string->str + string->len)
   {
    offset = val - string->str;
    precount = 0;

    pstring_maybe_expand (string, len);
    val = string->str + offset;

    if (pos < string->len)
      memmove (string->str + pos + len, string->str + pos, string->len - pos);


    if (offset < pos)
     {
      precount = MIN (len, pos - offset);
      memcpy (string->str + pos, val, precount);
     }


    if (len > precount)
      memcpy (string->str + pos + precount, val + precount + len,\
              len - precount);

   }
  else
   {
    pstring_maybe_expand (string, len);

    if (pos < string->len)
      memmove (string->str + pos + len, string->str + pos, string->len - pos);


    if (len == 1)
      string->str[pos] = *val;
    else
      memcpy (string->str + pos, val, len);
   }

  string->len += len;

  string->str[string->len] = 0;

  return string;

}

pstring*
pstring_append_len (pstring *string, char *val, long len)
{
  return pstring_insert_len (string, -1, val, len);
}

pstring*
pstring_append (pstring *string, char *val)
{
  return pstring_insert_len (string, -1, val, -1);
}

void
pstring_clear (pstring *string)
{

  memset (string->str, 0, string->len);
  string->len = 0;
    
}

void
pstring_free (pstring *string)
{

  free (string->str);

  string->allocated_len = 0;
  string->len = 0;
  string->str = NULL;

  free (string);

}

